package cn.stu.cms.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.stu.cms.dto.ChangePwdDto;
import cn.stu.cms.dto.LoginDto;
import cn.stu.cms.dto.PageDto;
import cn.stu.cms.dto.UserSaveDto;
import cn.stu.cms.mapper.*;
import cn.stu.cms.pojo.*;
import cn.stu.cms.service.UsersService;
import cn.stu.cms.util.BeanUtils;
import cn.stu.cms.util.MinioTool;
import cn.stu.cms.util.R;
import cn.stu.cms.util.UserHolder;
import cn.stu.cms.vo.EmployeeData;
import cn.stu.cms.vo.UsersVo;
import cn.stu.cms.vo.routes.GetMenuVo;
import cn.stu.cms.vo.routes.MenuVo;
import cn.stu.cms.vo.routes.Meta;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import static cn.stu.cms.common.CacheConstants.ROUTER_CACHE;
import static cn.stu.cms.common.CommonConstants.*;
import static cn.stu.cms.util.CollUtils.uniqueList;
import static cn.stu.cms.util.JwtHelper.createToken;

/**
 * (Users)表服务实现类
 *
 * @author gh
 * @since 2023-07-21 17:11:52
 */
@Service
@AllArgsConstructor
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {

    private EmployeesMapper employeesMapper;

    private RoleMenuMapper rmMapper;

    private MenuMapper menuMapper;

    private RolesMapper rolesMapper;

    private MinioTool minioTool;

    /**
     * 通过主键删除数据
     *
     * @param userId 主键
     * @return 是否成功
     */
    @Override
    public R<Boolean> deleteById(Long userId) {
        Users user = lambdaQuery().eq(Users::getUserId, userId).one();
        if (user == null) {
            return R.failed("用户" + NO_EXIST_MSG);
        }
        if (ADMIN.equals(user.getAccount())) {
            return R.failed(DISALLOWED_OPERATION);
        }
        baseMapper.deleteById(userId);
        LambdaUpdateWrapper<Employees> employeeUpdate = new LambdaUpdateWrapper<>();
        employeeUpdate.eq(Employees::getUserId, userId);
        employeesMapper.delete(employeeUpdate);
        return R.ok(DEL_OK_MSG);
    }

    /**
     * 登录
     *
     * @param loginDto 登录数据
     * @return {@link R}
     */
    @Override
    public R<String> login(LoginDto loginDto) {
        Users user = lambdaQuery().eq(Users::getAccount, loginDto.getAccount()).one();
        if (user == null) {
            return R.failed("用户名密码错误");
        }
        if (user.getStatus() != 0) {
            return R.failed("此账号被封禁，请联系管理员");
        }
        boolean login = user.getPassword().equals(loginDto.getPassword());
        if (!login) {
            return R.failed("用户名密码错误");
        }
        return R.ok(createToken(user.getUserId()), "登录成功");
    }

    @Override
    public R<Page<UsersVo>> queryPage(PageDto<UsersVo> page, UserSaveDto dto) {
        baseMapper.queryPage(page, dto);
        return R.ok(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> add(UserSaveDto userSaveDto) {
        Users users = BeanUtils.copier(userSaveDto, Users.class);
        Employees employee = BeanUtils.copier(userSaveDto, Employees.class);
        Long userId = IdWorker.getId();
        employee.setUserId(userId);
        users.setUserId(userId);
        users.setPassword(DEFAULT_PASSWORD);
        baseMapper.insert(users);
        employeesMapper.insert(employee);
        return R.ok(SAVE_OK_MSG);
    }

    /**
     * 编辑
     *
     * @param userSaveDto 用户将数据保存到
     * @return {@link R}<{@link Boolean}>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> edit(UserSaveDto userSaveDto) {
        if (userSaveDto.getUserId() == null) {
            return R.failed(NO_PARAMS_MSG);
        }
        Users user = lambdaQuery().eq(Users::getUserId, userSaveDto.getUserId()).one();
        if (user == null) {
            return R.failed("用户" + NO_EXIST_MSG);
        }
        if (userSaveDto.getRoleId() == ADMIN_ID && userSaveDto.getStatus() == 1) {
            return R.failed("超管账号不可禁用");
        }
        if (!userSaveDto.getAccount().equals(user.getAccount())) {
            // 账号是否重复
            if (lambdaQuery().eq(Users::getAccount, userSaveDto.getAccount()).count() > ONE) {
                return R.failed("此账号已经被使用");
            }
        }
        Users users = BeanUtils.copier(userSaveDto, Users.class);
        Employees employee = BeanUtils.copier(userSaveDto, Employees.class);

        baseMapper.updateById(users);
        LambdaUpdateWrapper<Employees> employeesUpdate = new LambdaUpdateWrapper<>();
        employeesUpdate.eq(Employees::getUserId, employee.getUserId());
        employeesMapper.update(employee, employeesUpdate);
        return R.ok(EDIT_OK_MSG);
    }

    /**
     * 按ID查询
     *
     * @param userId 用户ID
     * @return {@link R}<{@link UsersVo}>
     */
    @Override
    public R<UsersVo> queryById(Long userId) {
        UsersVo usersVo = baseMapper.queryById(userId);
        if (usersVo == null) {
            return R.ok(GET_INFO_OK);
        }
        Roles roles = rolesMapper.selectById(usersVo.getRoleId());
        if (roles != null) {
            usersVo.setRoleName(roles.getRoleName());
        }
        if (StrUtil.isNotBlank(usersVo.getUserImage())) {
            usersVo.setUserImage(minioTool.fileServer + PATH_SEPERATOR + minioTool.avatarBucketName + PATH_SEPERATOR + userId + PATH_SEPERATOR + usersVo.getUserImage());
        }
        return R.ok(usersVo, GET_INFO_OK);
    }

    @Override
    public R<Boolean> changePwd(ChangePwdDto changePwdDto) {
        Long userId = UserHolder.getUser().getUserId();
        Users user = lambdaQuery().eq(Users::getUserId, userId).one();
        if (!user.getPassword().equals(changePwdDto.getPassword())) {
            return R.failed("密码错误");
        }
        lambdaUpdate().set(Users::getPassword, changePwdDto.getNewPassword())
                .eq(Users::getUserId, userId)
                .update();
        return R.ok(EDIT_OK_MSG);
    }

    @Override
    @Cacheable(value = ROUTER_CACHE, key = "'roleId:'+#roleId")
    public R<List<GetMenuVo>> getRoutes(Long roleId) {
        Long userId = UserHolder.getUser().getUserId();
        Users user = lambdaQuery().eq(Users::getUserId, userId).eq(Users::getStatus, 0).one();
        if (user == null) {
            return R.failed(USER_STATUS_EXCEPTION);
        }
        if (user.getRoleId() == null) {
            return R.ok(GET_INFO_OK);
        }
        return R.ok(dynamicRoute(user.getRoleId()), GET_INFO_OK);
    }

    /**
     * 动态路由
     * 据角色id查出所拥有的权限的菜单id生成动态路由表
     *
     * @param roleId 角色标识
     * @return {@link List}<{@link GetMenuVo}>
     */
    private List<GetMenuVo> dynamicRoute(Long roleId) {
        // 查询当前角色所有权限
        List<RoleMenu> rmList = rmMapper.selectList(new LambdaQueryWrapper<RoleMenu>().eq(RoleMenu::getRoleId, roleId));
        List<Long> midAll = rmList.stream().map(RoleMenu::getMenuId).distinct().sorted().collect(Collectors.toList());
        // 取出所有权限Id并查询对应菜单信息
        List<Menu> menusAll = getMenuList(midAll);
        // 获取主菜单Id列表
        List<Long> mIds = menusAll.stream().map(Menu::getParentId).distinct().collect(Collectors.toList());
        // 根据主菜单Id查询主菜单信息，并将其加入菜单列表
        List<Menu> menusMain = getMenuList(mIds);
        // 不为空则取并集
        if (CollUtil.isNotEmpty(menusMain)) {
            menusAll.addAll(menusMain);
        }
        List<GetMenuVo> gMvo = new ArrayList<>();
        // 遍历查询并封装数据
        for (Menu m : menusAll) {
            GetMenuVo rmv = new GetMenuVo();
            Long mainId;
            if (m.getParentId() == 0) {
                mainId = m.getMenuId();
                // 查询主菜单数据
                BeanCopier menuToGetMenuVo = BeanCopier.create(Menu.class, GetMenuVo.class, false);
                menuToGetMenuVo.copy(m, rmv, null);
                rmv.setMeta(new Meta(m.getTitle(), m.getIcon()));
            } else {
                continue;
            }
            // 打包子菜单数据
            ArrayList<MenuVo> menuVos = new ArrayList<>();
            for (Menu child : menusAll) {
                if (Objects.equals(child.getParentId(), mainId)) {
                    BeanCopier menuToMenuVo = BeanCopier.create(Menu.class, MenuVo.class, false);
                    MenuVo menuVo = new MenuVo();
                    menuToMenuVo.copy(child, menuVo, null);
                    menuVo.setMeta(new Meta(child.getTitle(), child.getIcon()));
                    menuVos.add(menuVo);
                }
            }
            rmv.setChildren(uniqueList(menuVos));
            gMvo.add(rmv);
        }
        return gMvo;
    }

    /**
     * 获取菜单列表
     *
     * @param mIds M ID
     * @return {@link List}<{@link Menu}>
     */
    private List<Menu> getMenuList(List<Long> mIds) {
        return menuMapper.selectList(new LambdaQueryWrapper<Menu>().in(Menu::getMenuId, mIds));
    }


    @Override
    public EmployeeData getEmployeeData() {
        Long userId = UserHolder.getUser().getUserId();
        Users user = lambdaQuery().eq(Users::getUserId, userId).one();
        Employees employees = employeesMapper.selectOne(new LambdaQueryWrapper<Employees>().eq(Employees::getUserId, userId));
        if (ADMIN_ID == user.getRoleId()) {
            return new EmployeeData(true, employees.getTeamId(), employees.getEmployeeId(), userId);
        }
        return new EmployeeData(false, employees.getTeamId(), employees.getEmployeeId(), userId);
    }
}
